package lines.gameengine.strategy;

import lines.gameengine.FieldPoint;
import lines.gameengine.IBall;
import lines.gameengine.IField;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import static lines.gameengine.strategy.FieldOrientation.*;

/**
 * @author vpotapenko
 */
public class LinesFinder {

    private static final Map<FieldOrientation, Map<Boolean, Integer>> xTransformer =
            new HashMap<FieldOrientation, Map<Boolean, Integer>>();
    private static final Map<FieldOrientation, Map<Boolean, Integer>> yTransformer =
            new HashMap<FieldOrientation, Map<Boolean, Integer>>();
    static {
        Map<Boolean, Integer> part = new HashMap<Boolean, Integer>();
        part.put(false, 0);
        part.put(true, 0);
        xTransformer.put(VERTICAL, part);

        part = new HashMap<Boolean, Integer>();
        part.put(false, -1);
        part.put(true, 1);
        xTransformer.put(HORIZONTAL, part);
        xTransformer.put(FORWARD_DIAGONAL, part);
        xTransformer.put(BACKWARD_DIAGONAL, part);

        part = new HashMap<Boolean, Integer>();
        part.put(false, 0);
        part.put(true, 0);
        yTransformer.put(HORIZONTAL, part);

        part = new HashMap<Boolean, Integer>();
        part.put(false, -1);
        part.put(true, 1);
        yTransformer.put(VERTICAL, part);
        yTransformer.put(FORWARD_DIAGONAL, part);

        part = new HashMap<Boolean, Integer>();
        part.put(false, 1);
        part.put(true, -1);
        yTransformer.put(BACKWARD_DIAGONAL, part);
    }

    private final Collection<FieldPoint> currentLine = new HashSet<FieldPoint>();
    private final IField field;

    public LinesFinder(IField field) {
        this.field = field;
    }

    public void find(FieldPoint point, Collection<FieldPoint> lines) {
        for (FieldOrientation orientation : FieldOrientation.values()) {
            find(point, orientation, lines);
        }
    }

    private void find(FieldPoint point, FieldOrientation orientation, Collection<FieldPoint> lines) {
        currentLine.clear();
        currentLine.add(point);

        find(point, orientation, true);
        find(point, orientation, false);

        if (currentLine.size() >= field.getContext().getParameters().getLineSize())
            lines.addAll(currentLine);
    }

    private void find(FieldPoint point, FieldOrientation orientation, boolean ascend) {
        FieldPoint peer = getPeer(point, orientation, ascend);
        while (peer != null && equalValues(peer, point)) {
            currentLine.add(peer);
            peer = getPeer(peer, orientation, ascend);
        }
    }

    private boolean equalValues(FieldPoint point1, FieldPoint point2) {
        if (field.getCell(point1).isEmpty() || field.getCell(point2).isEmpty()) return false;

        IBall ball1 = field.getCell(point1).getBall();
        IBall ball2 = field.getCell(point2).getBall();

        return ball1.getValue() == ball2.getValue();
    }

    private FieldPoint getPeer(FieldPoint point, FieldOrientation orientation, boolean ascend) {
        int x = point.getX() + xTransformer.get(orientation).get(ascend);
        int y = point.getY() + yTransformer.get(orientation).get(ascend);

        return field.isValidPoint(x, y) ? field.getCell(x, y).getPoint() : null;
    }
}
